#include "audio_stream.h"
#include "adpcm.h"
#include "demux.h"
#include "driver_audio.h"

typedef struct
{
	void* hpcm;
	struct adpcm_state adpcmstate;

#ifdef WIN32
//#define SAVE_AUDIO_STREAM
#ifdef SAVE_AUDIO_STREAM
	FILE* fpcm;
#endif
#endif

}AUDIO_CONTEXT;

AUDIO_CONTEXT* g_audio_context = 0;

VIDEO_ERROR audio_stream_init()
{
	if (g_audio_context)
	{
		return VIDEO_ERROR_01;
	}

	g_audio_context = (AUDIO_CONTEXT*)MALLOC(sizeof(AUDIO_CONTEXT));
	if (g_audio_context == 0)
	{
		return VIDEO_ERROR_02;
	}

	memset(g_audio_context, 0, sizeof(AUDIO_CONTEXT));

	return VIDEO_ERROR_OK;
	
}

VIDEO_ERROR audio_stream_free()
{
	if (g_audio_context)
	{
		FREE(g_audio_context);
		g_audio_context = 0;
	}
	return VIDEO_ERROR_OK;
}

VIDEO_ERROR audio_stream_encode(short* indata, u32 len1, char* outdata, u32* len2)
{
	int sample_cnt = len1 / 2;
	adpcm_encoder(indata, outdata, sample_cnt, &g_audio_context->adpcmstate);
	*len2 = sample_cnt / 2;
	return VIDEO_ERROR_OK;
}


VIDEO_ERROR audio_stream_decode(char* indata, u32 len1, short* outdata, u32* len2)
{
	int sample_cnt = len1 * 2;
	adpcm_decoder(indata, outdata, sample_cnt, &g_audio_context->adpcmstate);
	*len2 = sample_cnt * sizeof(short);
	return VIDEO_ERROR_OK;
}

VIDEO_ERROR audio_stream_frame_out(FRAME* frame)
{
	//ADPCM to PCM, and render this buff
	void* buff = frame->buff;
	u32 len = frame->h.size;

	switch (g_avi_context->AudioType)
	{
	case 1:
		break;

	case 2:
	case 17:
		{
			frame->buff = mem_get_buff();
			audio_stream_decode((char*)buff, len, frame->buff, &len);
			frame->h.size = len;
			break;
		}

	default:
		assert(0);
		break;
	}

#ifdef SAVE_AUDIO_STREAM
		if (g_audio_context->fpcm)fwrite(frame->buff, 1, len, g_audio_context->fpcm);
#endif
	
	audio_driver_write(frame->buff, &len);

	return VIDEO_ERROR_OK;
}

VIDEO_ERROR audio_stream_frame_in(FRAME* frame)
{
	void* buff = frame->buff;
	u32 len = frame->h.size;

	switch (g_avi_context->AudioType)
	{
	case 1:
		break;

	case 2:
	case 17:
		{
			frame->buff = mem_get_buff();
			audio_stream_encode((short*)buff, len, frame->buff, &len);
			frame->h.size = len;
			break;
		}

	default:
		assert(0);
		break;
	}

	return VIDEO_ERROR_OK;
}

VIDEO_ERROR audio_driver_open(int flag)
{
	if (flag)
	{//waveout
		driver_audio_open_opt_t cfg1 =
		{
			{
				g_avi_context->Channels, //2,
				2, //16 bits
				g_avi_context->SampleRate, //16000,
				PCM_CHUNK,
				CHUNK_CUT,
				0,
				0,
			},

			0,
		};

		g_audio_context->hpcm = dev_Open("/dev/audio0", 0, &cfg1);
	}
	else
	{//wavein
		driver_audio_open_opt_t cfg1 =
		{
			{
				CHANNELS, //2,
				SAMPLESIZE, //16 bits
				SAMPLERATE, //16000,
				PCM_CHUNK,
				CHUNK_CUT,
				0,
				0,
			},

			0,
			AUDIO_INPUT_DMIC0F,
			0,
			0
		};

		g_audio_context->hpcm = dev_Open("/dev/audio1", 0, &cfg1);
	}

#ifdef SAVE_AUDIO_STREAM
	char pcm_name[MAXPATH];
	GetModuleFileName(NULL, pcm_name, MAXPATH);
	PathRemoveFileSpec(pcm_name);
	PathRemoveFileSpec(pcm_name);
	strcat_s(pcm_name, MAXPATH, "\\data\\Video\\avi_audio.pcm");
	g_audio_context->fpcm = fopen(pcm_name, "wb");
#endif

	if (g_audio_context->hpcm)
		return VIDEO_ERROR_OK;
	else
		return VIDEO_ERROR_11;
}

VIDEO_ERROR audio_driver_close(int flag)
{
	flag = flag;
	if (g_audio_context->hpcm)
	{
		dev_Close(&g_audio_context->hpcm);
	}

#ifdef SAVE_AUDIO_STREAM
	if (g_audio_context->fpcm)
	{
		fclose(g_audio_context->fpcm);
		g_audio_context->fpcm = 0;
	}
#endif

	return VIDEO_ERROR_OK;
}

VIDEO_ERROR audio_driver_read(u8* buff, u32* len)
{
	assert(PCM_CHUNK == *len);
	*len = dev_Read(g_audio_context->hpcm, buff, PCM_CHUNK, 0);
	return VIDEO_ERROR_OK;
}

VIDEO_ERROR audio_driver_write(u8* buff, u32* len)
{
	u32 ss = PCM_CHUNK * sizeof(u16) * g_avi_context->Channels;
	u32 cnt = (*len) / ss;
	u32 res_size = ((*len) - cnt * ss) / (sizeof(u16) * g_avi_context->Channels);
	*len = 0;
	for (u32 i = 0; i < cnt; i++)
	{
		*len += dev_Write(g_audio_context->hpcm, &buff[i * ss], PCM_CHUNK, 0);
	}
	if (res_size)
	{
		*len += dev_Write(g_audio_context->hpcm, &buff[cnt * ss], res_size, 0);
	}

	return VIDEO_ERROR_OK;
}
